
using System;
using System.Diagnostics.Contracts;
using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;
using UnityEngine;

public static class CullingHandle
{
   /// <summary>
   /// boundTest 
   /// </summary>
   /// <param name="planes"></param>
   /// <param name="boundsMin"></param>
   /// <param name="boundsMax"></param>
   /// <param name="testIntersection"></param>
   /// <returns>返回测试AABB是否在视锥体内</returns>
   public static bool AABBInternalFast(NativeArray<Plane> planes,  float3 boundsMin, float3 boundsMax)
   {
      float3 vmin = float3.zero;

      foreach (var t in planes)
      {
         var normal = t.normal;
         var planerDistance = t.distance;

         vmin.x = math.select(boundsMin.x, boundsMax.x, normal.x > 0);
         vmin.y = math.select(boundsMin.y, boundsMax.y, normal.y > 0);
         vmin.z = math.select(boundsMin.z, boundsMax.z, normal.z > 0);

         var dotValue = math.dot(normal, vmin); //得到点相对于法线的方向以及距离
         if (dotValue + planerDistance < 0) return false; // 相加后小于0 意味着物体穿出平面了
      }

      return true;
   }
}





[BurstCompile]
public class CullingHandleByJob : IDisposable
{
   private NativeArray<XyPlaneData> _planes;
   private NativeArray<CullingData> _objectData;
   private NativeArray<int> _result;

   public void Init(NativeArray<Matrix4x4> posList)
   {
      
      _planes = new NativeArray<XyPlaneData>(6, Allocator.Persistent);

      _result = new NativeArray<int>(posList.Length, Allocator.Persistent);

      _objectData = new NativeArray<CullingData>(posList.Length, Allocator.Persistent);

      for (int i = 0; i < posList.Length; i++)
      {
         var aabb = new AABB();
         aabb.Init(posList[i].GetPosition(),posList[i].lossyScale);
         _objectData[i] = new CullingData()
         {
            aabb = aabb
         };
      }
   }
   
   public NativeArray<int> GetResult(Camera _camera,out int resultCount)
   {
      var planes = GeometryUtility.CalculateFrustumPlanes(_camera);
      //NativeArray<Plane>.Copy(planes,_planes);
      
      for (int i = 0; i < planes.Length; i++)
      {
         _planes[i] = new XyPlaneData()
         {
            normal = planes[i].normal,
            distance = planes[i].distance
         };
      }

      float batchNum = math.min(32, _objectData.Length);
      
      var batchCount = new NativeArray<int>(Mathf.CeilToInt(_objectData.Length / batchNum), Allocator.TempJob);
      
      var handleJob = new CullingJobHandle()
      {
         planes = _planes,
         cullingDatas = _objectData,
         results = _result,
         batchCount = batchCount,
         batchNum = batchNum 
      };

      JobHandle handle = handleJob.Schedule(_objectData.Length, (int)batchNum);
      handle.Complete();

      resultCount = 0;
      foreach (var c in batchCount) resultCount += c;
      
      batchCount.Dispose();
      

      return _result;
   }


   public Matrix4x4[] GetResultMatrix(NativeArray<int> result,NativeArray<Matrix4x4> mats,int resultCount)
   {
      var handleJob = new JobResultHandle();
      handleJob.Init(result,mats,resultCount);
      var handle = handleJob.Schedule();
      handle.Complete();
      
      var matArray = new Matrix4x4[handleJob.GetCount()];
      NativeArray<Matrix4x4>.Copy(handleJob.visibleObjectMatrixs,matArray,handleJob.GetCount());
      
      handleJob.Dispose();
      return matArray;
   }
   public void Dispose()
   {
      _planes.Dispose();
      _objectData.Dispose();
      _result.Dispose();
   }
}


[BurstCompile]
public struct AABB
{
    public float3 Min;
    public float3 Max;
    
     
    public void Init(float3 center, float3 size)
    {
        Min = center - size * 0.5f;
        Max = center + size * 0.5f;

    }

    public float SqrDistance(float3 pos)
    {
        float sqrDistance = 0;
        for (int i = 0; i < 3; i++)
        {
            float v = pos[i];
            if (v < Min[i]) sqrDistance += (Min[i] - v) * (Min[i] - v);
            if (v > Max[i]) sqrDistance += (v - Max[i]) * (v - Max[i]);
        }

        return sqrDistance;
    }
}

[BurstCompile]
public struct CullingData
{
   public AABB aabb;
}


[BurstCompile]
public struct XyPlaneData
{
   public float3 normal;
   public float distance;
}


[BurstCompile]
public struct CullingJobHandle : IJobParallelFor
{
   
   [ReadOnly] public MapData _MapData; 
   [ReadOnly] public NativeArray<CullingData> cullingDatas;
   [ReadOnly] public NativeArray<XyPlaneData> planes;
   [ReadOnly] public float batchNum;
   [WriteOnly] public NativeArray<int> results;

   [NativeDisableParallelForRestriction]
   public NativeArray<int> batchCount;

   public void Execute(int index)
   {
      var inCamera = AABBInternalFast(planes, cullingDatas[index].aabb.Min, cullingDatas[index].aabb.Max);
      if (inCamera == 1)
      {
         int batchIndex = (int) math.floor(index / batchNum);
         batchCount[batchIndex] += 1;
      }
      results[index] = inCamera;
   }
   
   private int AABBInternalFast(NativeArray<XyPlaneData> planes, float3 boundsMin, float3 boundsMax)
   {
      float3 vmin = float3.zero;

      foreach (var t in planes)
      {
         float3 normal = t.normal;
         float planerDistance = t.distance;
      
         vmin.x = math.select(boundsMin.x, boundsMax.x, normal.x > 0);
         vmin.y = math.select(boundsMin.y, boundsMax.y, normal.y > 0);
         vmin.z = math.select(boundsMin.z, boundsMax.z, normal.z > 0);
      
         float dotValue = math.dot(normal, vmin); //得到点相对于法线的方向以及距离
         if (dotValue + planerDistance < 0) return 0; // 相加后小于0 意味着物体穿出平面了
      }

      return 1;
   }
   
}

[BurstCompile]
public struct JobResultHandle : IJob
{

   [ReadOnly] private NativeArray<int> _resulArray;
   [ReadOnly] private NativeArray<Matrix4x4> _transforms;
   [WriteOnly] public NativeArray<Matrix4x4> visibleObjectMatrixs;
   public NativeArray<int> Count;

   public void Init(NativeArray<int> resulArray,NativeArray<Matrix4x4> transforms,int resultCount)
   {
      _resulArray = resulArray;
      _transforms = transforms;
      visibleObjectMatrixs = new NativeArray<Matrix4x4>(resultCount,Allocator.TempJob);
      Count = new NativeArray<int>(1,Allocator.TempJob);
      Count[0] = 0;
   }
   public void Execute()
   {
      
      for (int i = 0; i < _resulArray.Length; i++)
      {
         if (_resulArray[i] == 1)
         {
            visibleObjectMatrixs[GetCount()]=_transforms[i];
            Count[0] += 1;
         } 
         
      }
   }

   public int GetCount()
   {
      return Count[0];
   }

   public void Dispose()
   {
      visibleObjectMatrixs.Dispose();
      Count.Dispose();
   }
}



public struct JobVisibleHandle : IJobParallelFor
{
   public void Execute(int index)
   {
      
   }
}